package xx;

import java.io.IOException;
import java.util.List;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;

import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;

/**
 * 使用selenium web自动化测试工具套 进行显式和隐式调用
 * 可以解决的问题和场景有很多，这里主要用来做 webjs加密破解
 * 1，使用java ScriptEngine 来执行js
 *  -- java ScriptEngine 不支持 浏览器内置对象，nodejs也不支持
 * 解决方法：
 * 2，java通过浏览器内核(selenium 工具套)来执行js
 * @author zhaigt
 * 
 */
public class TestJava2Js {
	
	/**
	 * 获取java 提供的 ScriptEngine 脚本执行引擎 信息
	 * @throws IOException
	 */
	@Test
	public void showScriptEngine() throws IOException {
		ScriptEngineManager manager = new ScriptEngineManager();
		List<ScriptEngineFactory> factories = manager.getEngineFactories();
		for (ScriptEngineFactory f : factories) {
			System.out.println(f.getEngineName());
			System.out.println(f.getEngineVersion());
			System.out.println(f.getLanguageName());
			System.out.println(f.getLanguageVersion());
			System.out.println(f.getExtensions());
			System.out.println(f.getMimeTypes());
			System.out.println(f.getNames());
		}
	}
	  
	/**
	 * java调用js 解析执行js脚本(js文件)代码
	 * 场景：
	 * 1，使用js特有的优势
	 * 2,js特有的一些加密方式，并且js代码进行混淆了，转换为java方式 代价比较高的情况下
	 * @throws Exception
	 */
	@Test
	public void testjava2js() throws Exception {
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName("javascript");
		
		// 如果需要引用 js外部文件，可以通过流把js文件(jquery.js)读到一个String变量
		
		/*
		String jsFileName = "C:\\jquery.min.js"; // 读取js文件
		FileReader reader = new FileReader(jsFileName); // 执行指定脚本
		engine.eval(reader);
		*/
		String script = "function add(op1,op2){return op1+op2};var res1=2,res=10; "; //定义函数并调用
		engine.eval(script);
		Invocable invoke = (Invocable) engine; // 调用方法，并传入两个参数
		
		// 方式1 通过对象调用方法， 获取结果
		Object c = invoke.invokeFunction("add", 1,2);
		System.out.println(c);
		
		// 方式2 执行js脚本调用方法， 获取结果
		engine.eval("var res = add(2,3);");
		
		// 获取新定义的变量，会覆盖原有同名变量
		Object o = engine.get("res");
		System.out.println(o);
		
		// 获取 原有脚本/脚本文件 中的 变量
		Object o2 = engine.get("res1");
		System.out.println(o2);
		
		// 测试 浏览器 内置对象 是否支持
		try {
			engine.eval("alert(2);");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		try {
			engine.eval("document.write(2);");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		try {
			engine.eval("var innerHeight=window.innerHeight");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		try {
			engine.eval("var userAgent=navigator.userAgent");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		// ...so， ScriptEngine 不支持 浏览器内置对象；nodejs 也一样不支持  浏览器内置对象
	}
	
	/**
	 * 方式1；使用 ChromeDriver 显式调用 浏览器
	 * 当然这里 也可以用firefox IE等其他 浏览器和diver，下载不同的浏览器和驱动即可
	 * java 通过 selenium 工具套-->打开本地浏览器-->输入html文件地址(支持远程和本地)-->执行html中的脚本代码-->获取结果
	 * 这里 获取的结果 不是 html代码，而是 在浏览器解析完html js代码之后展示的信息（这里可能不太好理解，一会我画个图 单独描述下，html js jsp执行的时机问题）
	 * 详细描述：
	 * java通过 selenium 工具套 ，让js脚本在浏览器内核中执行，然后 获取返回结果
	 * 这样就解决了 java ScriptEngine 不支持浏览器内置对象的问题
	 * 如此一样js代码 与实际web运行的环境一样执行
	 * @throws Exception
	 */
	@Test
	public void testSelenium0() throws Exception {
		// 第一步：因为是 使用chromediver所以 需要先安装chrome浏览器,chromediver
		// 这个博客介绍的很详细，地址：https://www.testwo.com/blog/6931
		
		// 第二步：设置环境变量
		System.setProperty("webdriver.chrome.driver",
				"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe");
		ChromeDriver webDriver = new ChromeDriver();
		
		long start = System.currentTimeMillis();
		
		// 第三步：设置访问地址
		//----
		// 这里和浏览器 地址栏目 可以输入的 地址一样 支持 远程地址 和 本地地址
		//webDriver.get("http://www.51jdy.cn");
		//webDriver.get("http://localhost:8080/web/Noname2.html?pwd=98912&username=halou");
		webDriver.get("file:///Users/guangtaozhai/Documents/workspace/web/WebContent/Noname2.html?pwd=98912&username=halou");
		
		// 第四步：获取输出信息；html中的js代码执行后 在body中document.write()或者 赋值给body或者div
		// 元素定位 获取 <html> 中<body> 下面的 内容
		WebElement webElement = webDriver.findElement(By.xpath("/html/body"));
		System.out.println(webElement.getText());
		System.out.println("耗时："+(System.currentTimeMillis()-start));
		///---
		webDriver.close();
	}
	
	/**
	 * 方式2：隐式调用(java 后台调用浏览器/selenium/webdriver)
	 * 因为 方式1 会打开本地浏览器，这样一是速度慢,二是 不满足后台执行的方式 比如 部署在linux服务器上以后
	 * 方式2 通过 selenium 提供的 HtmlUnitDriver 进行后台调用；执行效率大幅上升
	 * @throws Exception
	 */
	@Test
	public void testSelenium1() throws Exception {
		// 使用HtmlUnitDriver 是不需要 安装 浏览器 和 驱动支持
		HtmlUnitDriver webDriver = new HtmlUnitDriver();
		webDriver.setJavascriptEnabled(true); // 设置支持 js脚本解析 ，是不是跟 安卓的 webview设置很像？
		
		long start = System.currentTimeMillis();
		//---- 
		// 这里和浏览器 地址栏目 可以输入的 地址一样 支持 远程地址 和 本地地址
		//webDriver.get("http://www.51jdy.cn");
		//webDriver.get("http://localhost:8080/web/Noname2.html?pwd=98912&username=halou");
		webDriver.get("file:///Users/guangtaozhai/Documents/workspace/java2js/WebContent/Noname2.html?pwd=98912&username=halou");
				
		WebElement webElement = webDriver.findElement(By.xpath("/html/body"));
		System.out.println(webElement.getText() );
		System.out.println(System.currentTimeMillis()-start);
		///---
		webDriver.close();
	}
	
	//-----------------------------------------------------------
	
	/**
	 * 包装的一个方法 通过jsp 提供webservice服务(懒的配置springmvc框架了)
	 * 部署web项目到tomcat
	 * 启动后浏览器 访问地址：http://localhost:8080/java2js/NewFile.jsp?pwd=98912&username="+username
	 * @param pwd
	 * @param keyStr
	 * @return
	 * @throws Exception
	 */
	public static String testSelenium0(String pwd,String username) throws Exception {
//		System.setProperty("webdriver.chrome.driver",
//				"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe");
		HtmlUnitDriver webDriver = new HtmlUnitDriver();
		webDriver.setJavascriptEnabled(true);
		
		long start = System.currentTimeMillis();
		//----
		String url = "http://localhost:8080/java2js/Noname2.html?pwd="+pwd+"&username="+username;
		webDriver.get(url);
		System.out.println(url);
		//webDriver.get("file:///Users/guangtaozhai/Documents/workspace/web/WebContent/Noname2.html?pwd="+pwd+"&username="+username);
		
		WebElement webElement = webDriver.findElement(By.xpath("/html/body"));
		String res = webElement.getText();
		System.out.println(webElement.getText() );
		System.out.println(System.currentTimeMillis()-start);
		///---
		webDriver.close();
		return res;
	}
	
}
